package com.springlearn.springsourcecode.chap5;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @author wtcode
 * @Description 自定义扫描component注解
 * @date 2025/3/28 - 14:48
 */
public class ComponentScannerPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        try {

            CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
            String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
            for (String definitionName : beanDefinitionNames) {

                BeanDefinition beanDefinition = beanFactory.getBeanDefinition(definitionName);
                MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(beanDefinition.getBeanClassName());

                AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
                if(annotationMetadata.hasAnnotation(ComponentScan.class.getName())){
                    ComponentScan annotation = AnnotationUtils.synthesizeAnnotation(
                            annotationMetadata.getAnnotationAttributes(ComponentScan.class.getName()),
                            ComponentScan.class, null
                    );
                    if(annotation != null){
                        String[] packages = annotation.basePackages();
                        for (String aPackage : packages) {
                            String path = "classpath*:" + aPackage.replace(".","/") + "/**/*.class";
                            Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                            AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); // bean名称生成器
                            for (Resource resource : resources) {
                                if(!resource.isReadable()) continue;
                                MetadataReader metadataReader1 = metadataReaderFactory.getMetadataReader(resource);
                                if (isCandidateComponent(metadataReader1)){
                                    if(metadataReader1.getAnnotationMetadata().hasAnnotation(Component.class.getName())){
                                        AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(metadataReader1.getClassMetadata().getClassName()).getBeanDefinition();
                                        String name = beanNameGenerator.generateBeanName(bd, (DefaultListableBeanFactory) beanFactory);
                                        ((DefaultListableBeanFactory) beanFactory).registerBeanDefinition(name,bd);
                                    }
                                }



                            }


                        }
                    }
                }

            }

        } catch (IOException e) {
            e.printStackTrace();
        }


    }


    private boolean isCandidateComponent(MetadataReader reader) {
        ClassMetadata classMetadata = reader.getClassMetadata();
        return classMetadata.isConcrete()
                && !classMetadata.isInterface()
                && !classMetadata.isAbstract();
    }
}
